package main

import (
	"fmt"
)

/*
切片的原理

*/

func main() {

	slice := []int{1, 2, 3, 4, 5}
	Test(slice)

	//TODO:Go中都是值传递，没有采用引用传递。所以你在写的时根据实际情况该传引用的时候就考虑使用指针
	//问题：请问输出结果是 1,2,3,4，5 还是 1,2,3,4，5,1,2,3,4，5
	//答案：前者
	//fmt.Println(slice)

	/*
		TODO:slice的数据结构：
		//32位操作系统中是4字节  64位操作系统中是8字节
		//所有内存地址 是一个无符号十六进制整型数据
		type slice struct{
		 array unsafe.Pointer //指针 8字节
		 len   int     //长度  8字节
		 cap   int     //容量  8字节
		}
	*/

	/*
		源码在：runtime.slice.go 中的 growslice()
		TODO:扩容问题，如果超过1024 按照上一次的1/4进行扩容，如果没有超过是按上一次的2倍
		TODO:如果长度小于等于容量添加数据元素，地址不会发生拷贝
		TODO:如果长度大于容量添加数据元素，地址一定会发生拷贝
	*/
	//TestSliceAppend()

	/*
		TODO:slice拷贝使用 copy, 源码在runtime.slicecopy()
		他属于深拷贝
	*/
	TestSliceCopy()
}

func Test(slice []int) {
	slice = append(slice, 1, 2, 3, 4, 5)
}

/**
测试切片的长度大于容量时，发生内存拷贝操作
*/
func TestSliceAppend() {
	slice := []int{1, 2, 3}

	/*
			 TODO:特别注意这里所说的长度和容量的比较时机
			 cap:指的是slice没有append扩容之前的容量
			 len:指的是slice通过append扩容之后的长度
			 TODO:特别注意你不能用append之后去取cap,len来比较，一个slice的len永远是小于cap的
		     分析如下打印：

			第一次 内存地址:0xc00000c360,len:3,cap:3
			第二次 内存地址:0xc0000103c0,len:4,cap:6
			第三次 内存地址:0xc0000103c0,len:5,cap:6
			第四次 内存地址:0xc0000103c0,len:6,cap:6
			第五次 内存地址:0xc00001a180,len:7,cap:12

			第一次：初始化 len==cap
			第二次：append前的cap=3,append后的len=4 因此4>3,发生内存拷贝
			第三次：append前的cap=6,append后的len=5,因此5<6,不发生内存拷贝
	*/

	fmt.Printf("第一次 内存地址:%p,len:%d,cap:%d\n", slice, len(slice), cap(slice))
	//通过append扩容
	slice = append(slice, 4)
	fmt.Printf("第二次 内存地址:%p,len:%d,cap:%d\n", slice, len(slice), cap(slice))

	slice = append(slice, 4)
	fmt.Printf("第三次 内存地址:%p,len:%d,cap:%d\n", slice, len(slice), cap(slice))

	slice = append(slice, 4)
	fmt.Printf("第四次 内存地址:%p,len:%d,cap:%d\n", slice, len(slice), cap(slice))

	slice = append(slice, 4)
	fmt.Printf("第五次 内存地址:%p,len:%d,cap:%d\n", slice, len(slice), cap(slice))
}

/**
测试切片的copy
*/
func TestSliceCopy() {
	s1 := []int{1, 2}
	s2 := make([]int, 3)
	copy(s2, s1)
	fmt.Printf("s1内存地址:%p\ns2内存地址：%p\n", s1, s2)
}
